#include <stddef.h>
#include <stdlib.h>
#include <esp_err.h>
#include <esp_log.h>
#include "lcd_function.h"
#include "lcd_support.h"
#include "lcd_def.h"
#include "freertos/FreeRTOS.h"
#include "lcd_spi.h"

#define LCD_SCREEN_SIZE      (lcd_wide * lcd_high)
#define LCD_FRAME_BYTE_TOTAL (lcd_wide * lcd_high * 2)

static uint16_t lcd_wide,lcd_high;
static uint8_t *lcd_frame_buff = NULL;
static SemaphoreHandle_t xMutex = NULL;

void lcd_refresh_mutex_release(void)
{
    xSemaphoreGive(xMutex);
}


void lcd_frame_buff_init(int lcd_w, int lcd_h)
{
    xMutex = xSemaphoreCreateMutex();
    ESP_ERROR_CHECK(xMutex == NULL);

    lcd_frame_buff = (uint8_t *)malloc(sizeof(uint16_t) *  lcd_w * lcd_h);
    ESP_ERROR_CHECK(lcd_frame_buff == NULL);

    lcd_wide = lcd_w;
    lcd_high = lcd_h;
    lcd_set_address(0,0,lcd_wide-1, lcd_high-1);
}

void lcd_frame_clear(uint16_t color)
{
    uint16_t * p = (uint16_t *)lcd_frame_buff;
    for (int i = 0; i < LCD_SCREEN_SIZE; i++) {
        p[i] = color;
    }
}

void lcd_draw_point (uint16_t x, uint16_t y, uint16_t color)
{
    lcd_set_address(x, y, x, y);
    LCD_WR_HalfWord(color);
}

void lcd_frame_draw_point (uint16_t x, uint16_t y, uint16_t color)
{
    //uint16_t * p = (uint16_t *)lcd_frame_buff;
    *((uint16_t *)lcd_frame_buff + (y*lcd_wide + x)) = (color & 0xff00) >> 8 | (color & 0x00ff) << 8;
}

void lcd_frame_refresh(void)
{
#define DMA_SINGLE_TRANSFER_BYTE_MAX 32768
    if(xSemaphoreTake(xMutex, 0) != pdTRUE)
    {
        return;
    }
    int total_byte = LCD_FRAME_BYTE_TOTAL;
    uint8_t *p = lcd_frame_buff;
    for (int send_byte = 0; send_byte < total_byte;)
    {
        int remaining = total_byte - send_byte;
        int chunk_size = (remaining >= DMA_SINGLE_TRANSFER_BYTE_MAX)
                         ? DMA_SINGLE_TRANSFER_BYTE_MAX
                         : remaining;

        bsp_spi_write_data(chunk_size, p + send_byte);
        send_byte += chunk_size;
    }
}
/**
 * @brief       设置显示窗口
 * @param       xs:窗口列起始地址
 * @param       ys:坐标行起始地址
 * @param       xe:窗口列结束地址
 * @param       ye:坐标行结束地址
 * @retval      无
 */
void lcd_set_address(uint16_t xs, uint16_t ys, uint16_t xe, uint16_t ye)
{
#if USE_HORIZONTIAL == 0
    LCD_WR_REG(0x2a); /* 列地址设置 */
    LCD_WR_HalfWord(xs+0x0C);
    LCD_WR_HalfWord(xe+0x0C);
    LCD_WR_REG(0x2b); /* 行地址设置 */
    LCD_WR_HalfWord(ys);
    LCD_WR_HalfWord(ye);
    LCD_WR_REG(0x2c); /* 储存器写 */
#elif USE_HORIZONTIAL == 1
    LCD_WR_REG(0x2a); /* 列地址设置 */
    LCD_WR_HalfWord(xs+0x0E);
    LCD_WR_HalfWord(xe+0x0E);
    LCD_WR_REG(0x2b); /* 行地址设置 */
    LCD_WR_HalfWord(ys);
    LCD_WR_HalfWord(ye);
    LCD_WR_REG(0x2c); /* 储存器写 */
#elif USE_HORIZONTIAL == 2
    LCD_WR_REG(0x2a); /* 列地址设置 */
    LCD_WR_HalfWord(xs);
    LCD_WR_HalfWord(xe);
    LCD_WR_REG(0x2b); /* 行地址设置 */
    LCD_WR_HalfWord(ys+0x0E);
    LCD_WR_HalfWord(ye+0x0E);
    LCD_WR_REG(0x2c); /* 储存器写 */
    #else
    LCD_WR_REG(0x2a); /* 列地址设置 */
    LCD_WR_HalfWord(xs);
    LCD_WR_HalfWord(xe);
    LCD_WR_REG(0x2b); /* 行地址设置 */
    LCD_WR_HalfWord(ys+0x0C);
    LCD_WR_HalfWord(ye+0x0C);
    LCD_WR_REG(0x2c); /* 储存器写 */
#endif
}
/**
 * @brief       指定颜色填充区域
 * @param       xs:填充区域列起始地址
 * @param       ys:填充区域行起始地址
 * @param       xe:填充区域列结束地址
 * @param       ye:填充区域行结束地址
 * @param       color:填充颜色值
 * @retval      无
 */
void LCD_Fill(uint16_t xs, uint16_t ys, uint16_t xe, uint16_t ye, uint16_t color)
{
    uint16_t i, j;
    lcd_set_address(xs, ys, xe - 1, ye - 1);
    for (j = ys; j < ye; j++)
    {
        for (i = xs; i < xe; i++)
        {
            LCD_WR_HalfWord(color);
        }
    }
}
/**
 * @brief       图片显示函数
 * @param       x:图片显示位置列起始坐标
 * @param       y:图片显示位置行起始坐标
 * @param       width:图片宽度
 * @param       height:图片高度
 * @param       pic:图片取模数组
 * @retval      无
 */

void lcd_show_picture(uint16_t x, uint16_t y, uint16_t width, uint16_t height, const uint8_t pic[])
{
    uint8_t picH, picL;
    uint16_t i, j;
    uint32_t k = 0;
    lcd_set_address(x, y, x + width - 1, y + height - 1);
    for (i = 0; i < height; i++)
    {
        for (j = 0; j < width; j++)
        {
            picH = pic[k * 2];
            picL = pic[k * 2 + 1];
            LCD_WR_HalfWord(picL << 8 | picH);
            k++;
        }
    }
}

void lcd_frame_show_picture( uint16_t width, uint16_t height, const uint8_t pic[])
{
    uint16_t i, j;
    uint32_t k = 0;
    for (i = 0; i < height; i++)
    {
        for (j = 0; j < width; j++)
        {
            uint16_t  color =   pic[k * 2] |  pic[k * 2 + 1] << 8 ;
            lcd_frame_draw_point(j,i,color);
            k++;
        }
    }
}
